// Written by Sam Tarin, Andi Cescolini, and Kevin Sonnen
// gameServer.cpp
//

#include "gameServer.h"

using boost::asio::ip::tcp;

namespace networkO{

////////////////////////////////////////
// class game_room Functions
////////////////////////////////////////

  void game_room::join(game_participant_ptr participant)
  {
    #ifdef DEBUG
    std::cout << "Game room join" << std::endl;
    #endif
    participants_.insert(participant);
  }

  void game_room::leave(game_participant_ptr participant)
  {
    #ifdef DEBUG
    std::cout << "Game room leave" << std::endl;
    #endif
    participants_.erase(participant);
  }

  void game_room::deliver(const sit_wrap::situation_wrapper& sit)
  {
    #ifdef DEBUG
    std::cout << "Game room deliver" << std::endl;
    std::cout << sit.data() << std::endl;
    #endif
    recent_sits.push_back(sit);
    while (recent_sits.size() > max_recent_sits)
      recent_sits.pop_front();

    std::for_each(participants_.begin(), participants_.end(),
        boost::bind(&game_participant::deliver, _1, boost::ref(sit)));
  }

////////////////////////////////////////
// class game_session Functions
////////////////////////////////////////

  tcp::socket& game_session::socket()
  {
    #ifdef DEBUG
    std::cout << "Game session socket" << std::endl;
    #endif
    return socket_;
  }

  void game_session::start()
  {
    #ifdef DEBUG
    std::cout << "Game session start" << std::endl;
    #endif
    room_.join(shared_from_this());
    boost::asio::async_read(socket_,
        boost::asio::buffer(read_sit_.data(), sit_wrap::situation_wrapper::header_length),
          boost::bind(
          &game_session::handle_read_header, shared_from_this(),
          boost::asio::placeholders::error));
  }

  void game_session::deliver(const sit_wrap::situation_wrapper& sit)
  {
    #ifdef DEBUG
    std::cout << "Game session deliver" << std::endl;
    std::cout << "Game session deliver data" << sit.data() << std::endl;
    #endif
    bool write_in_progress = !write_sits_.empty();
    write_sits_.push_back(sit);
    if (!write_in_progress)
    {
      boost::asio::async_write(socket_,
          boost::asio::buffer(write_sits_.front().data(),
            write_sits_.front().length()),
          boost::bind(&game_session::handle_write, shared_from_this(),
            boost::asio::placeholders::error));
    }
  }

  void game_session::handle_read_header(const boost::system::error_code& error)
  {
    #ifdef DEBUG
    std::cout << "Game session read header" << std::endl;
    std:: cout << read_sit_.data() << std::endl;
    if (error)
	std::cout << "We have an error, number: " << error.message() << std::endl;
    #endif
    if (!error && read_sit_.decode_header())
    {
      boost::asio::async_read(socket_,
          boost::asio::buffer(read_sit_.body(), read_sit_.body_length()),
          boost::bind(&game_session::handle_read_body, shared_from_this(),
            boost::asio::placeholders::error));
    }
    else
    {
      #ifdef DEBUG
      std::cout << "ERROR: LEAVING" << std::endl;
      #endif
      room_.leave(shared_from_this());
    }
  }

  void game_session::handle_read_body(const boost::system::error_code& error)
  {
    #ifdef DEBUG
    std::cout << "Game session read body" << std::endl;
    std::cout << read_sit_.data() << std::endl;
    #endif
    if (!error)
    {
      room_.deliver(read_sit_);
      char data[1024] = {'\0'};
      memset(read_sit_.data(), 0, read_sit_.body_length()+4);
	
	
      size_t r_length;
      boost::asio::async_read(socket_,
          boost::asio::buffer(read_sit_.data(), sit_wrap::situation_wrapper::header_length),
          boost::bind(&game_session::handle_read_header, shared_from_this(),
            boost::asio::placeholders::error));
#ifdef DEBUG
    std::cout << "********situation after memset********" << std::endl;
    std::cout << read_sit_.data() << std::endl;
    #endif
 	
    }
    else
    {
      room_.leave(shared_from_this());
    }
  }

  void game_session::handle_write(const boost::system::error_code& error)
  {
    #ifdef DEBUG
    std::cout << "Game session handle write" << std::endl;
    #endif
    if (!error)
    {
      write_sits_.pop_front();
      if (!write_sits_.empty())
      {
        #ifdef DEBUG
	std::cout << "printing FROM SERVER data" << write_sits_.front().data() << std::endl;
	std::cout << "printing length: " << write_sits_.front().length() << std::endl;
        #endif
        boost::asio::async_write(socket_,
            boost::asio::buffer(write_sits_.front().data(),
              write_sits_.front().length()),
            boost::bind(&game_session::handle_write, shared_from_this(),
              boost::asio::placeholders::error));
      }
    }
    else
    {
      room_.leave(shared_from_this());
    }
  }

////////////////////////////////////////
// class game_server Functions
////////////////////////////////////////

  game_server::game_server(boost::asio::io_service& io_service,
      const tcp::endpoint& endpoint)
    : io_service_(io_service),
      acceptor_(io_service, endpoint)
  {
    #ifdef DEBUG
    std::cout << "Game server constructor" << std::endl;
    #endif
    game_session_ptr new_session(new game_session(io_service_, room_));
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&game_server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }
  void game_server::handle_accept(game_session_ptr session,
      const boost::system::error_code& error)
  {
    #ifdef DEBUG
    std::cout << "Game server handle accept" << std::endl;
    #endif
    if (!error)
    {
      session->start();
      game_session_ptr new_session(new game_session(io_service_, room_));
      acceptor_.async_accept(new_session->socket(),
          boost::bind(&game_server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }
  }
} // End namespace networkO
